using System;
using WiMo.Games.Inputs;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Xna.Framework.Input;

namespace WiMo.Games.StandardInput
{
    public class InputManager : IInputManager
    {
        private const int INT_MENU_BAR_HEIGHT = 24;
        private Dictionary<Keys, KeyPressInfo> _keysDown = new Dictionary<Keys, KeyPressInfo>();
        private AutoResetEvent _drawEvent;
        private bool _isMouseUp;
        private static bool accLeftButtonUp = true;
        private static bool accRightButtonUp = true;
        private static bool accUpButtonUp = true;
        private static bool accDownButtonUp = true;
        private List<TouchPoint> _lastTouchPoints = new List<TouchPoint>();
        private Rectangle _leftSoftbuttonRectangle;
        private Rectangle _rightSoftbuttonRectangle;
        private Drawables.ISpriteShape _rectangle;

        public InputManager(IDisplay displayManager)
        {
            DisplayManager = displayManager;
            AccelerometerDPadThreshold = new Vector2(0.1f, 0.1f);

            _drawEvent = new AutoResetEvent(true);

            SoftMenuFontColor = Color.White;

            _leftSoftbuttonRectangle = new Rectangle(0,
                (int)((displayManager.TargetSize.Height - INT_MENU_BAR_HEIGHT)),
                (int)(displayManager.TargetSize.Width / 2),
                (int)(24 * displayManager.ScaleFactor.Y));

            _rightSoftbuttonRectangle = new Rectangle(_leftSoftbuttonRectangle.Width, _leftSoftbuttonRectangle.Top, _leftSoftbuttonRectangle.Width, _leftSoftbuttonRectangle.Height);

            _rectangle = new MenuBar();
            _rectangle.Origin = WiMo.Games.Drawables.Origin.TopLeft;
            _rectangle.Scale = displayManager.ScaleFactor;
            _rectangle.Position = new PointF(0, (int)(displayManager.TargetSize.Height - INT_MENU_BAR_HEIGHT));
            _rectangle.Size = new Size(displayManager.TargetSize.Width, INT_MENU_BAR_HEIGHT);

            MenuBarBackgroundColor = Color.Transparent;
        }

        private InputManager() { }

        public Vector3 GVector { get; private set; }
        public ICharacterBuffer CharacterBuffer { get; private set; }
        public bool SupportAccelerometer { get { { return true; } } }
        public bool SupportMultiTouch { get { { return true; } } }
        public bool UseAccelerometerAsThumbPad { get; set; }
        public Vector2 AccelerometerDPadThreshold { get; set; }
        public string LeftSoftButton { get; set; }
        public string RightSoftButton { get; set; }
        public Color SoftMenuFontColor { get; set; }
        public List<TouchPoint> TouchPoints { get { return _lastTouchPoints; } }

        public void StartHopper()
        {

        }

        public void StopHopper()
        {

        }

        public Color MenuBarBackgroundColor
        {
            get
            {
                return _rectangle.FillColor;
            }
            set
            {
                _rectangle.FillColor = value;
                _rectangle.DrawInformation = null;
            }
        }

        public ISpriteFont SoftMenuFont { get; set; }
        public IDisplay DisplayManager { get; set; }

        internal void KeyDown(Keys key)
        {
            KeyDown(key, false);
        }

        internal void KeyDown(Keys key, bool fromAccelerometer)
        {
            _drawEvent.WaitOne();
            if (!_keysDown.ContainsKey(key))
            {
                _keysDown.Add(key, new KeyPressInfo(key, fromAccelerometer));
                //_charBuffer.Add(key);
            }
            _drawEvent.Set();
        }

        internal void KeyUp(Keys key, bool fromAccelerometer)
        {
            _drawEvent.WaitOne();
            _keysDown.Remove(key);
            _drawEvent.Set();
        }

        internal void KeyUp(Keys key)
        {
            KeyUp(key, false);
        }

        public bool WasPressed(IInputManager inputManager, Keys key)
        {
            return (IsKeyDown(key) && (inputManager == null || inputManager.IsKeyUp(key)));
        }

        public bool IsPressed(IInputManager inputManager, Keys key, int debounceMs, bool fromAccelerometer)
        {
            KeyPressInfo keyInfo;
            if (_keysDown.TryGetValue(key, out keyInfo))
            {
                return keyInfo.Debounce(debounceMs) && keyInfo.FromAccelerometer == fromAccelerometer;
            }
            else
                return false;
        }

        public bool IsPressed(IInputManager inputManager, Keys key, int debounceMs)
        {
            KeyPressInfo keyInfo;
            if (_keysDown.TryGetValue(key, out keyInfo))
                return keyInfo.Debounce(debounceMs);
            else
                return false;
        }

        public bool IsKeyDown(Keys key)
        {
            return _keysDown.ContainsKey(key);
        }

        public bool IsKeyUp(Keys key)
        {
            return !_keysDown.ContainsKey(key);
        }

        public bool IsMouseUp
        {
            get { return _isMouseUp; }
        }

        
        private Location ScalePoint(Vector2 point)
        {
            return new Location((point.X - DisplayManager.DisplayOffset.X) / DisplayManager.ScaleFactor.X, (point.Y - DisplayManager.DisplayOffset.Y) / DisplayManager.ScaleFactor.Y);
        }

        public bool WasScreenTouched(IInputManager inputManager)
        {
            if (inputManager == null)
                return false;

            return (!_isMouseUp && inputManager.IsMouseUp);
        }

        public bool WasScreenTouched(IInputManager inputManager, Rectangle rect)
        {
            if (inputManager == null)
                return false;

            foreach (var touchLocation in _lastTouchPoints)
            {
                if (touchLocation.State == TouchPoint.TouchLocationState.Pressed && rect.Contains(ScalePoint(touchLocation.Position)))
                    return true;
            }

            return false;
        }

        public IInputManager GetKeysState()
        {
            return this.Clone();
        }

        public void Update()
        {
            var points = Microsoft.Xna.Framework.Input.TouchPanel.GetState();
            _lastTouchPoints.Clear();
            _isMouseUp = true;
            foreach (var touchLocation in points)
            {
                if (touchLocation.State == TouchLocationState.Pressed)
                {
                    _isMouseUp = false;

                    var clickPoint = ScalePoint(new Vector2(touchLocation.Position.X, touchLocation.Position.Y));

                    if (_leftSoftbuttonRectangle.Contains(clickPoint) && !string.IsNullOrEmpty(LeftSoftButton))
                        KeyDown(Keys.F1);

                    if (_rightSoftbuttonRectangle.Contains(clickPoint) && !string.IsNullOrEmpty(RightSoftButton))
                        KeyDown(Keys.F2);
                }

                if (touchLocation.State == TouchLocationState.Released)
                {
                    if (_keysDown.ContainsKey(Keys.F1))
                        _keysDown.Remove(Keys.F1);

                    if (_keysDown.ContainsKey(Keys.F2))
                        _keysDown.Remove(Keys.F2);
                }

                _lastTouchPoints.Add(new TouchPoint(touchLocation.Id, touchLocation.Position.X, touchLocation.Position.Y, touchLocation.Pressure, (TouchPoint.TouchLocationState)touchLocation.State));
            }

            var accelState = Microsoft.Xna.Framework.Input.Accelerometer.GetState();
            GVector = new Vector3(accelState.Acceleration.X, accelState.Acceleration.Y, accelState.Acceleration.Z);

            if (!UseAccelerometerAsThumbPad)
                return;

            if (accLeftButtonUp && GVector.X < -AccelerometerDPadThreshold.X) { accLeftButtonUp = false; KeyDown(Keys.Left, true); }
            if (!accLeftButtonUp && GVector.X > -AccelerometerDPadThreshold.X) { accLeftButtonUp = true; KeyUp(Keys.Left, true); }

            if (accRightButtonUp && GVector.X > AccelerometerDPadThreshold.X) { accRightButtonUp = false; KeyDown(Keys.Right, true); }
            if (!accRightButtonUp && GVector.X < AccelerometerDPadThreshold.X) { accRightButtonUp = true; KeyUp(Keys.Right, true); }

            if (accUpButtonUp && GVector.Y < -AccelerometerDPadThreshold.Y) { accUpButtonUp = false; KeyDown(Keys.Down, true); }
            if (!accUpButtonUp && GVector.Y > -AccelerometerDPadThreshold.Y) { accUpButtonUp = true; KeyUp(Keys.Down, true); }

            if (accDownButtonUp && GVector.Y > AccelerometerDPadThreshold.Y) { accDownButtonUp = false; KeyDown(Keys.Up, true); }
            if (!accDownButtonUp && GVector.Y < AccelerometerDPadThreshold.Y) { accDownButtonUp = true; KeyUp(Keys.Up, true); }
        }


        public void Draw(GameTime gameTime)
        {
            if (MenuBarBackgroundColor != Color.Transparent)
                DisplayManager.Add(_rectangle);

            if (!string.IsNullOrEmpty(LeftSoftButton))
                DisplayManager.DrawText(new PointF(DisplayManager.TargetSize.Width / 4, DisplayManager.TargetSize.Height - 10), LeftSoftButton, SoftMenuFont, SoftMenuFontColor, WiMo.Games.Drawables.Origin.Center);

            if (!string.IsNullOrEmpty(RightSoftButton))
                DisplayManager.DrawText(new PointF((3 * DisplayManager.TargetSize.Width / 4), DisplayManager.TargetSize.Height - 10), RightSoftButton, SoftMenuFont, SoftMenuFontColor, WiMo.Games.Drawables.Origin.Center);
        }

        private IInputManager Clone()
        {
            var inputManager = new InputManager();
            _drawEvent.WaitOne();
            foreach (var key in _keysDown)
                inputManager._keysDown.Add(key.Key, key.Value.Clone());

            foreach (var touchPoint in _lastTouchPoints)
                inputManager._lastTouchPoints.Add(touchPoint.Clone());

            inputManager._isMouseUp = _isMouseUp;

            _drawEvent.Set();

            return inputManager;
        }
    }
}
